/*
 * Copyright (c) 2024-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "iotc_event.h"
#include "iotc_oh_ble.h"
#include "iotc_oh_sdk.h"
#include "iotc_oh_device.h"
#include "securec.h"
#include "cJSON.h"
#include "iotc_entry.h"
#include "iotc_log.h"
//new
#include "parameter.h"
#include "util.h"

#if IOTC_CONF_AILIFE_SUPPORT != 0
#define HILINK_PSK_LEN  16
#define PINCODE_LEN     8
#endif

#define ADV_TIMEOUT     UINT32_MAX

#define DEMO_LOG(...) IotcLogOutputInner(IOTC_LOG_LEVEL_ERROR, IOTC_FILE_NAME, IOTC_FUNC_NAME, __LINE__, __VA_ARGS__)

static IotcDeviceInfo DEV_INFO = {
    .sn = "FFEE3333",
#if IOTC_CONF_AILIFE_SUPPORT
    .prodId = "2EKT",
#else
    .prodId = "00007",
#endif
    .subProdId = "",
    .model = "PD31",
#if IOTC_CONF_AILIFE_SUPPORT
    .devTypeId = "094",
#else
    .devTypeId = "1007",
#endif
    .devTypeName = "TabletPD31",
    .manuId = "104",
    .manuName = "OpenValley",
    .fwv = "1.0.0",
    .hwv = "1.0.0",
    .swv = "1.0.0",
    .protType = IOTC_PROT_TYPE_BLE,
};

static const IotcServiceInfo SVC_INFO[] = {
    {"switch", "switch"},
    {"gear", "gear"},
    {"gps","gps"},
#if IOTC_CONF_AILIFE_SUPPORT
    {"deviceTime", "deviceTime"}
#endif
};

const char *PIN_CODE = "01234567";

const uint8_t AC_KEY[IOTC_AC_KEY_LEN] = {
    0x49, 0x3F, 0x45, 0x4A, 0x3A, 0x72, 0x38, 0x7B, 0x36, 0x32, 0x50, 0x3C, 0x49, 0x39, 0x62, 0x38,
    0x72, 0xCB, 0x6D, 0xC5, 0xAE, 0xE5, 0x4A, 0x82, 0xD3, 0xE5, 0x6D, 0xF5, 0x36, 0x82, 0x62, 0xEB,
    0x89, 0x30, 0x6C, 0x88, 0x32, 0x56, 0x23, 0xFD, 0xB8, 0x67, 0x90, 0xA7, 0x7B, 0x61, 0x1E, 0xAE
};

static bool g_switch = false;

static int32_t SwitchPutCharState(const IotcServiceInfo *svc, const char *data, uint32_t len)
{
    DEMO_LOG("SwitchPutCharState in");
    if (data == NULL || len == 0) {
        DEMO_LOG("param invalid");
        return -1;
    }
    cJSON *json = cJSON_Parse(data);
    if (json == NULL) {
        DEMO_LOG("parse error");
        return -1;
    }

    cJSON *item = cJSON_GetObjectItem(json, "on");
    DEMO_LOG("SwitchPutCharState cJSON_GetObjectItem");
    if (item == NULL || !cJSON_IsNumber(item)) {
        cJSON_Delete(json);
        DEMO_LOG("get on error");
        return -1;
    }

    int32_t on = cJSON_GetNumberValue(item);
    DEMO_LOG("switch on put %d=>%d", g_switch, on);
    g_switch = on == 0 ? false : true;
    if(on == 0){//new
        system("power-shell suspend");
    }else{//new
        system("power-shell wakeup");
    }
    cJSON_Delete(json);
    return 0;
}

void SetScreenState(bool isScreenOn)//new
{
    DEMO_LOG("SwitchGetCharState in");
    DEMO_LOG("isScreenOn is %d",isScreenOn);
    g_switch = isScreenOn;
    DEMO_LOG("g_switch is %d",g_switch);
}

static int32_t SwitchGetCharState(const IotcServiceInfo *svc, char **data, uint32_t *len)
{
    DEMO_LOG("SwitchGetCharState in");
    if (data == NULL || *data != NULL) {
        DEMO_LOG("param invalid");
        return -1;
    }

    cJSON *json = cJSON_CreateObject();
    if (json == NULL) {
        DEMO_LOG("create obj error");
        return -1;
    }
//new
    bool isScreenOn = GetScreenState();
    DEMO_LOG("isScreenOn is %d",isScreenOn);
    g_switch = isScreenOn;
//new end
    DEMO_LOG("SwitchGetCharState cJSON_CreateObject");
    if (cJSON_AddNumberToObject(json, "on", g_switch) == NULL) {
        cJSON_Delete(json);
        DEMO_LOG("add num error");
        return -1;
    }
    DEMO_LOG("SwitchGetCharState cJSON_AddNumberToObject");
    *data = cJSON_PrintUnformatted(json);
    cJSON_Delete(json);
    if (*data == NULL) {
        DEMO_LOG("json print error");
        return -1;
    }
    DEMO_LOG("switch on get %d", g_switch);
    *len = strlen(*data);
//new
    DEMO_LOG("SwitchGetCharState new in");
    int rptNum = 1;
    IotcCharState reportInfo[1] = {0};
    reportInfo->svcId = svc->svcId;
    reportInfo->len = *len;
    reportInfo->data = *data;
    int ret = IotcOhDevReportCharState(reportInfo, rptNum);
    if (ret != 0) {
        DEMO_LOG("IotcOhDevReportCharState error");
        return -1;
    }
//new end
    return 0;
}
//dzy
static double g_latitude = 30.268681;
static double g_longitude = 120.110690;

static int32_t GpsGetCharState(const IotcServiceInfo *svc, char **data, uint32_t *len)
{
    DEMO_LOG("GpsGetCharState in");
    if (data == NULL || *data != NULL) {
        DEMO_LOG("param invalid");
        return -1;
    }

    cJSON *json = cJSON_CreateObject();
    if (json == NULL) {
        DEMO_LOG("create obj error");
        return -1;
    }
    DEMO_LOG("GpsGetCharState cJSON_CreateObject");
    cJSON *latitudeItem = cJSON_CreateNumber(g_latitude);
    cJSON *longitudeItem = cJSON_CreateNumber(g_longitude);

    if (latitudeItem == NULL || longitudeItem == NULL) {
        cJSON_Delete(json);
        DEMO_LOG("create number error");
        return -1;
    }

    if (!cJSON_AddItemToObject(json, "latitude", latitudeItem) ||
        !cJSON_AddItemToObject(json, "longitude", longitudeItem)) {
        cJSON_Delete(json);
        DEMO_LOG("add item error");
        return -1;
    }
    DEMO_LOG("GpsGetCharState cJSON_AddItemToObject");
    *data = cJSON_PrintUnformatted(json);
    cJSON_Delete(json);
    if (*data == NULL) {
        DEMO_LOG("json print error");
        return -1;
    }

    DEMO_LOG("GPS location get: %s", *data);
    *len = strlen(*data);
//new
    DEMO_LOG("GpsGetCharState new in");
    int rptNum = 1;
    IotcCharState reportInfo[1] = {0};
    reportInfo->svcId = svc->svcId;
    reportInfo->len = *len;
    reportInfo->data = *data;
    int ret = IotcOhDevReportCharState(reportInfo, rptNum);
    if (ret != 0) {
        DEMO_LOG("IotcOhDevReportCharState error");
        return -1;
    }
//new end
    return 0;
}

static int32_t g_gear = 0;

static int32_t GearPutCharState(const IotcServiceInfo *svc, const char *data, uint32_t len)
{
    DEMO_LOG("GearPutCharState in");
    if (data == NULL || len == 0) {
        return -1;
    }
    cJSON *json = cJSON_Parse(data);
    if (json == NULL) {
        return -1;
    }

    cJSON *item = cJSON_GetObjectItem(json, "gear");
    if (item == NULL || !cJSON_IsNumber(item)) {
        cJSON_Delete(json);
        return -1;
    }

    int32_t gear = cJSON_GetNumberValue(item);
    DEMO_LOG("gear val put %d=>%d\n", g_gear, gear);
    g_gear = gear;

    cJSON_Delete(json);
    return 0;
}

static int32_t GearGetCharState(const IotcServiceInfo *svc, char **data, uint32_t *len)
{
    DEMO_LOG("GearGetCharState in");
    if (data == NULL || *data != NULL) {
        return -1;
    }

    cJSON *json = cJSON_CreateObject();
    if (json == NULL) {
        return -1;
    }

    if (cJSON_AddNumberToObject(json, "gear", g_gear) == NULL) {
        cJSON_Delete(json);
        return -1;
    }

    *data = cJSON_PrintUnformatted(json);
    cJSON_Delete(json);
    if (*data == NULL) {
        return -1;
    }
    DEMO_LOG("gear get %d\n", g_gear);
    *len = strlen(*data);
    return 0;
}

#if IOTC_CONF_AILIFE_SUPPORT
static int32_t ReportAll(void);

static int32_t DevTimePutCharState(const IotcServiceInfo *svc, const char *data, uint32_t len)
{
    DEMO_LOG("DevTimePutCharState in");
    if (data == NULL || len == 0) {
        return -1;
    }

    ReportAll();
    return 0;
}
#endif

const struct SvcMap {//here
    const IotcServiceInfo *svc;
    int32_t (*putCharState)(const IotcServiceInfo *svc, const char *data, uint32_t len);
    int32_t (*getCharState)(const IotcServiceInfo *svc, char **data, uint32_t *len);
} SVC_MAP[] = {
    {&SVC_INFO[0], SwitchPutCharState, SwitchGetCharState},
    {&SVC_INFO[1], GearPutCharState, GearGetCharState},
    {&SVC_INFO[2], NULL, GpsGetCharState},//dzy
#if IOTC_CONF_AILIFE_SUPPORT
    {&SVC_INFO[3], DevTimePutCharState, NULL},
#endif
};

static int32_t PutCharState(const IotcCharState state[], uint32_t num)
{
    DEMO_LOG("PutCharState in");
    if (state == NULL || num == 0) {
        DEMO_LOG("param invalid");
        return -1;
    }

    int32_t ret = 0;
    bool found = false;
    for (uint32_t i = 0; i < num; ++i) {
        DEMO_LOG("put char sid:%s data:%s", state[i].svcId, state[i].data);
        for (uint32_t j = 0; j < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++j) {
            if (strcmp(state[i].svcId, SVC_MAP[j].svc->svcId) != 0 || SVC_MAP[j].putCharState == NULL) {
                continue;
            }
            found = true;
            int32_t curRet = SVC_MAP[j].putCharState(SVC_MAP[j].svc, state[i].data, state[i].len);
            if (curRet != 0) {
                ret = curRet;
                DEMO_LOG("put char sid:%s error %d", state[i].svcId, ret);
            }
        }
    }
    return ret != 0 ? ret : (found ? 0 : -1);
}

static int32_t GetCharState(const IotcCharState state[], char *out[], uint32_t len[], uint32_t num)
{
    DEMO_LOG("GetCharState in");
    if (state == NULL || num == 0 || out == NULL || len == NULL) {
        DEMO_LOG("param invalid");
        return -1;
    }

    int32_t ret = 0;
    bool found = false;
    for (uint32_t i = 0; i < num; ++i) {
        DEMO_LOG("get char sid:%s", state[i].svcId);
        for (uint32_t j = 0; j < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++j) {
            if (strcmp(state[i].svcId, SVC_MAP[j].svc->svcId) != 0 || SVC_MAP[j].getCharState == NULL) {
                continue;
            }
            found = true;
            int32_t curRet = SVC_MAP[j].getCharState(SVC_MAP[j].svc, &out[i], &len[i]);
            if (curRet != 0) {
                ret = curRet;
                DEMO_LOG("get char sid:%s error %d", state[i].svcId, ret);
            }
        }
    }

    return ret != 0 ? ret : (found ? 0 : -1);
}

static int32_t ReportAll(void)
{
    DEMO_LOG("ReportAll in");
    IotcCharState reportInfo[sizeof(SVC_MAP) / sizeof(SVC_MAP[0])] = {0};
    int32_t ret = 0;
    uint32_t rptNum = 0;
    for (uint32_t i = 0; i < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++i) {
        DEMO_LOG("i is %d",i);
        if (SVC_MAP[i].getCharState == NULL) {
            DEMO_LOG("i is %d getCharState is null",i);
            continue;
        }
        reportInfo[rptNum].svcId = SVC_MAP[i].svc->svcId;
        DEMO_LOG("i is %d svcId is % s",i,SVC_MAP[i].svc->svcId);
        ret = SVC_MAP[i].getCharState(SVC_MAP[rptNum].svc, (char **)&reportInfo[rptNum].data, &reportInfo[rptNum].len);
        DEMO_LOG("ret is %d",ret);
        if (ret != 0) {
            DEMO_LOG("get char sid:%s error %d", reportInfo[rptNum].svcId, ret);
            break;
        }
        rptNum++;
    }
    if (ret == 0) {
        DEMO_LOG("IotcOhDevReportCharState start");
        ret = IotcOhDevReportCharState(reportInfo, rptNum);
        DEMO_LOG("IotcOhDevReportCharState end");
    }

    for (uint32_t i = 0; i < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++i) {
        if (reportInfo[i].data != NULL) {
            cJSON_free((char *)reportInfo[i].data);
            reportInfo[i].data = NULL;
        }
    }
    return ret;
}

static int32_t GetPincode(uint8_t *buf, uint32_t bufLen)
{
    DEMO_LOG("GetPincode in");
    if (buf == NULL || bufLen > IOTC_PINCODE_LEN) {
        DEMO_LOG("param invalid");
        return -1;
    }

#if IOTC_CONF_AILIFE_SUPPORT
    unsigned char pskBuf[HILINK_PSK_LEN] = {0};
    extern void HiLinkGetPsk(unsigned char *psk, unsigned short len);
    HiLinkGetPsk(pskBuf, HILINK_PSK_LEN);

    int32_t ret = memcpy_s(buf, bufLen, pskBuf, PINCODE_LEN);
#else
    int32_t ret = memcpy_s(buf, bufLen, PIN_CODE, strlen(PIN_CODE));
#endif
    if (ret != EOK) {
        return -1;
    }
    return 0;
}

static int32_t GetAcKey(uint8_t *buf, uint32_t bufLen)
{
    DEMO_LOG("GetAcKey in");
    if (buf == NULL || bufLen > IOTC_AC_KEY_LEN) {
        DEMO_LOG("param invalid");
        return -1;
    }
    int32_t ret = memcpy_s(buf, bufLen, AC_KEY, sizeof(AC_KEY));
    if (ret != EOK) {
        return -1;
    }
    return 0;
}

static void DemoBleEventListener(int32_t event)
{
    DEMO_LOG("DemoBleEventListener in");
    int32_t ret = 0;
    switch (event) {
        case IOTC_CORE_COMM_EVENT_MAIN_INITIALIZED:
        case IOTC_CORE_BLE_EVENT_GATT_DISCONNECT:
            ret = IotcOhBleStartAdv(ADV_TIMEOUT);
            break;
        default:
            return;
    }
    DEMO_LOG("event[%d] ret:%d", event, ret);
}

static int32_t NoticeReboot(IotcRebootReason res)
{
    DEMO_LOG("notice reboot res %d", res);
    return 0;
}

#define SET_OH_SDK_OPTION(ret, option, ...) \
    do { \
        (ret) = IotcOhSetOption((option), __VA_ARGS__); \
        if ((ret) != 0) { \
            DEMO_LOG("set option %d error %d", (option), (ret)); \
            return ret; \
        } \
    } while (0);

int32_t IotcOhDemoEntry(void)//here
{
    DEMO_LOG("IotcOhDemoEntry in");

    const char *serial = AclGetSerial();
    DEMO_LOG("iotc serial is %s", serial);
    DEV_INFO.sn = strdup(serial);//new
    int32_t ret = IotcOhDevInit();
    if (ret != 0) {
        DEMO_LOG("init device error %d", ret);
        return ret;
    }
//new end
    ret = IotcOhBleEnable();
    if (ret != 0) {
        DEMO_LOG("enable ble connect error %d", ret);
        return ret;
    }
    DEMO_LOG("iotc DEV_INFO.sn is %s", DEV_INFO.sn);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_PUT_CHAR_STATE_CALLBACK, PutCharState);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_GET_CHAR_STATE_CALLBACK, GetCharState);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_REPORT_ALL_CALLBACK, ReportAll);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_GET_PINCODE_CALLBACK, GetPincode);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_GET_AC_KEY_CALLBACK, GetAcKey);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_DATA_FREE_CALLBACK, cJSON_free);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_REBOOT_CALLBACK, NoticeReboot);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_DEV_INFO, &DEV_INFO);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_SVC_INFO, SVC_INFO, sizeof(SVC_INFO) / sizeof(SVC_INFO[0]));
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_BLE_START_UP_ADV_TIMEOUT, (1000 * 60 * 1000));//new
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_SDK_REG_EVENT_LISTENER, DemoBleEventListener);
    SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_SDK_CONFIG_PATH, "/data/app/iotc");//new

    ret = IotcOhMain();
    if (ret != 0) {
        DEMO_LOG("iotc oh main error %d", ret);
        return ret;
    }
    DEMO_LOG("iotc oh main success");
    return ret;
}

void IotcOhDemoExit(void)
{
    int32_t ret = IotcOhStop();
    if (ret != 0) {
        DEMO_LOG("iotc stop error %d", ret);
    }

    ret = IotcOhSetOption(IOTC_OH_OPTION_SDK_UNREG_EVENT_LISTENER, DemoBleEventListener);
    if (ret != 0) {
        DEMO_LOG("unreg event listener error %d", ret);
    }

    ret = IotcOhBleDisable();
    if (ret != 0) {
        DEMO_LOG("iotc wifi disable error %d", ret);
    }

    ret = IotcOhDevDeinit();
    if (ret != 0) {
        DEMO_LOG("iotc dev info deinit error %d", ret);
    }
}

void PressButton1(void)
{
    DEMO_LOG("button 1 press");
    IotcOhRestore();
}

void PressButton2(void)
{
    DEMO_LOG("button 2 press");
    IotcOhDemoExit();
}